#import <OneKit/OneKitApp.h>
#import <RangersAppLog/BDAutoTrack.h>
#import <RangersAPM/RangersAPM.h>
#import <RangersAPM/RangersAPM+DebugLog.h>
#import <RangersAPM/RangersAPM+ALog.h>
#import <RangersAPM/RangersAPM+UserException.h>
#import <RangersAPM/RangersAPM+EventMonitor.h>
#import <RangersAPM/RangersAPMConfig+NetworkPro.h>
#import <mach/mach.h>
#import "FlutterVolcenginePlugin.h"

@implementation FlutterVolcenginePlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterVolcenginePlugin* instance = [[FlutterVolcenginePlugin alloc]init];
    FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"flutter_volcengine" binaryMessenger:[registrar messenger]];
    [registrar addMethodCallDelegate:instance channel:channel];
    [instance makeAppCrash];
}

+ (void) startMonitorNativeWithLaunchOptions: (NSDictionary<id,id>*)launchOptions {
    [OneKitApp startWithLaunchOptions:launchOptions];
    NSString* appId = [[NSBundle mainBundle]infoDictionary][@"RANGERS_APM_APP_ID"];
    NSString* appToken = [[NSBundle mainBundle]infoDictionary][@"RANGERS_APM_APP_TOKEN"];
    NSString* appDomain = [[NSBundle mainBundle]infoDictionary][@"RANGERS_APM_DOMAIN"];
    NSString* appChannel = [[NSBundle mainBundle]infoDictionary][@"RANGERS_APM_CHANNEL"];
    
    RangersAPMConfig* config = [RangersAPMConfig configWithAppID:appId appToken:appToken];
    config.channel = appChannel;
    config.deviceIDSource = RAPMDeviceIDSourceFromRAppLog;
    config.defaultMonitors = RangersAPMCrashMonitorSwitch|RangersAPMLagMonitorSwitch|RangersAPMUserExceptionMonitorSwitch|RangersAPMNetworkMonitorSwitch|RangersAPMLaunchMonitorSwitch|RangersAPMFPSMonitorSwitch|RangersAPMUIMonitorSwitch|RangersAPMHybridMonitorSwitch|RangersAPMMemoryGraphMonitorSwitch|RangersAPMCrashProtectorSwitch;
    config.userID = [UIDevice currentDevice].identifierForVendor.UUIDString;
    config.enableTracing = true;
//    if ([appDomain isEqual:NULL]) {
//        config.domain = appDomain;
//    }
    [RangersAPM setALogEnabled];
    [RangersAPM enableConsoleLog];
#if DEBUG
    [RangersAPM allowDebugLogUsingLogger:^(NSString * _Nonnull log) {
        NSLog(@"RangersAPM: %@", log);
    }];
#endif
    [RangersAPM startWithConfig:config];
    
    BDAutoTrackConfig* autoTrackConfig = [BDAutoTrackConfig configWithAppID:appId launchOptions:launchOptions];
    autoTrackConfig.channel = appChannel;
    [BDAutoTrack startTrackWithConfig:autoTrackConfig];

}

- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
    dispatch_async(dispatch_get_main_queue(), ^{
           @try {
               if([@"makeAppCrash" isEqualToString:call.method]) {
                   [self makeAppCrash];
               } else if ([@"makeUserException" isEqualToString:call.method]) {
                   [self makeUserException];
               } else if ([@"makeAppFrozen" isEqualToString:call.method]) {
                   [self makeAppFrozen];
               } else if ([@"makeCPUException" isEqualToString:call.method]) {
                   [self makeCPUException];
               } else if ([@"makeAppOutOfMemeory" isEqualToString:call.method]) {
                   [self makeAppOutOfMemeory];
               } else {
                   result(FlutterMethodNotImplemented);
               }
           } @catch (NSException *e) {
               result([FlutterError errorWithCode:e.name message:e.reason details:e.userInfo]);
           }
       });
}

- (void)makeAppCrash {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSArray *array = [NSArray array];
            [array objectAtIndex:10];
        });
}

- (void)makeUserException {
    
    [RangersAPM trackAllThreadsLogExceptionType:@"testUserException"
                                   skippedDepth:0
                                 customParams:@{@"testCustomKey":@"testCustomValue"}
                                      filters:@{@"testFilterKey":@"testFilterValue"}
                                     callback:^(NSError * _Nullable error){
                                        NSLog(@"%@",error);
                                     }
    ];
}

- (void)makeAppFrozen {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        sleep(5);
    });
}


//设置的内存采集启动阈值，当APP内存超过此值时将启动内存优化模块，采集APP内存状态
static float dangerousMemoryThreshold = 1024.0;
        
//计算APP当前的内存占用，当内存占用超过内存采集启动阈值时，返回true，否则返回false
bool overMemoryThreshold(void)
{
    kern_return_t kr;

    task_vm_info_data_t task_vm;
    mach_msg_type_number_t task_vm_count = TASK_VM_INFO_COUNT;
    kr = task_info(mach_task_self_, TASK_VM_INFO, (task_info_t) &task_vm, &task_vm_count);

    if (kr == KERN_SUCCESS) {
        printf("Current App Memory is :%f\n\n", task_vm.phys_footprint / (1024.0 * 1024.0));
        if (task_vm.phys_footprint / (1024.0 * 1024.0) > dangerousMemoryThreshold) {
            return true;
        } else {
            return false;
        }
    }

    return false;
}

////触发内存泄漏，当APP当前内存占用小于内存采集启动阈值时，会不断触发内存泄漏
- (void)makeAppOutOfMemeory {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            if (!overMemoryThreshold()) {
                CGSize size = CGSizeMake(1024 * 8, 1024 * 8 * 9.0f/16.0);
                const size_t bitsPerComponent = 8;
                const size_t bytesPerRow = size.width * 4;
                CGContextRef ctx = CGBitmapContextCreate(calloc(sizeof(unsigned char), bytesPerRow * size.height), size.width, size.height, bitsPerComponent, bytesPerRow, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaPremultipliedLast);
                CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0);
                CGContextFillRect(ctx, CGRectMake(0, 0, size.width, size.height));
                sleep(1);
            } else {
                break;
            }
        }
    });
}

- (void)makeCPUException {
    for (int i = 0; i < 10; i++) {
        NSString *queueName = [NSString stringWithFormat:@"com.apmplus.testcpu%d",i];
        dispatch_queue_t queue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL);
        dispatch_async(queue, ^{
                int count = 0;
                while (1) {
                        count++;
                }
        });
    }
}

@end
